/*
 * Copyright (c) 2011, Nikolaus Moll
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * * Neither the name of the gradecalculator.net nor the
 *   names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
package net.gradecalculator.gui.qt.common.widgets;

import java.util.LinkedList;
import java.util.List;

import javax.swing.event.EventListenerList;

import net.gradecalculator.gui.qt.common.interfaces.IValidationListener;
import net.miblounge.miglayout.qt.MigLayout;

import com.trolltech.qt.gui.QComboBox;
import com.trolltech.qt.gui.QWidget;

public class MultiComboBox extends QWidget {
	
	private final List<QComboBox> boxes;
	private final MigLayout layout;
	private EventListenerList listeners = new EventListenerList();
	private final List<String> values;
	private final int maximumSelection;
	
	private final List<String> currentList = new LinkedList<String>();
	
	public MultiComboBox(final QWidget parent, final List<String> values)
	{
		this(parent, values, values.size());
	}

	public MultiComboBox(final QWidget parent, final List<String> values, final int maximumSelection)
	{
		super(parent);
		this.maximumSelection = maximumSelection;
		this.values = values;

		boxes = new LinkedList<QComboBox>();
		
		layout = new MigLayout("", "0[grow, 200:350:800]0", ""); 
		setLayout(layout);
		
		addComboBox();
	}

	protected synchronized void notifyValidate()
	{
		for (IValidationListener l : this.listeners.getListeners(IValidationListener.class)) {
			l.revalidate();
		}
	}
	
	public void addValidationListener(final IValidationListener listener)
	{
		this.listeners.add(IValidationListener.class, listener);
	}
	
	private void disableEvents()
	{
		for (QComboBox box : boxes) {
			try {
				box.currentIndexChanged.disconnect(this, "eventTextChanged()");
			}
			//CHECKSTYLE:OFF
			catch (final Exception e) {
			//CHECKSTYLE:ON
			}
		}
	}

	private void enableEvents()
	{
		for (QComboBox box : boxes) {
			box.currentIndexChanged.connect(this, "eventTextChanged()");
		}
	}
	
	private static QWidget getParentWindow(final QWidget widget)
	{
		QWidget parent = widget;
		while (parent != null && !parent.isWindow()) {
			parent = parent.parentWidget();
		}
		return parent;
	}
	
	private void setBoxContent(final QComboBox box, final String comboText, final List<String> selectedItems)
	{
		final List<String> strings = new LinkedList<String>();
		strings.add("");
		
		for (final String text : values) {
			if (comboText.equals(text) || !selectedItems.contains(text)) {
				strings.add(text);
			}
		}

		final int index = strings.indexOf(comboText);
		
		box.clear();
		for (final String type : strings) {
			box.addItem(type);
		}

		box.setCurrentIndex(index);
	}

	protected void eventTextChanged()
	{
		final List<String> selectedItems = new LinkedList<String>();
		for (final QComboBox box : boxes) {
			if (box.currentText().equals("")) {
				continue;
			}
			selectedItems.add(box.currentText());
		}

		setSelectedItems(selectedItems);
	}

	public void setSelectedItems(final List<String> selectedItems) {
		final List<String> itemsSelected = new LinkedList<String>();
		final List<String> itemsDeselected = new LinkedList<String>(currentList);
		currentList.clear();
		
		final List<String> strings = new LinkedList<String>(selectedItems);
		if (strings.size() < maximumSelection) {
			strings.add("");
		}

		while (strings.size() > boxes.size()) {
			addEmptyBox();
		}
		while (strings.size() < boxes.size()) { 
			removeComboBox();
		}
		
		disableEvents();
		int i = 0;
		for (QComboBox box : boxes) {
			final String text = strings.get(i++);
			setBoxContent(box, text, selectedItems);

			if (text.equals("")) {
				break;
			}

			currentList.add(text);
			if (itemsDeselected.contains(text)) {
				itemsDeselected.remove(text);
			} else {
				itemsSelected.add(text);
			}
		}
		enableEvents();

		getParentWindow(this).adjustSize();
		
		for (final String item : itemsDeselected) {
			selectionChangedEvent(item, false);
		}
		for (final String item : itemsSelected) {
			selectionChangedEvent(item, true);
		}
		
		notifyValidate();
	}
	
	private void removeComboBox()
	{
		final QComboBox box = boxes.get(boxes.size() - 1);
		boxes.remove(box);
		layout.removeWidget(box);
		box.setParent(null);
		box.close();
	}
	
	private void addEmptyBox()
	{
		final QComboBox box = new QComboBox(this);

		boxes.add(box);
		layout.addWidget(box, "grow, wrap");
		box.currentIndexChanged.connect(this, "eventTextChanged()");
	}
	
	protected void selectionChangedEvent(final String item, final boolean isSelected)
	{
	}
	
	private void addComboBox()
	{
		addEmptyBox();
		setSelectedItems(getSelectedItems());
	}
	
	public List<String> getSelectedItems()
	{
		return new LinkedList<String>(currentList);
	}

    public void setItemSelected(final String name, final boolean isSelected)
    {
        if (currentList.contains(name) == isSelected) {
            return;
        }

        final List<String> selectedItems = getSelectedItems();
        if (isSelected) {
            selectedItems.add(name);
        }
        else {
            selectedItems.remove(name);
        }

        setSelectedItems(selectedItems);
    }
      
}
